package com.example.kingdee.util;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.example.kingdee.common.StringUtils;
import com.example.kingdee.common.constant.YiTiConstant;
import com.example.kingdee.config.RuoYiConfig;
import com.example.kingdee.dto.PurchaseSyncDto;
import com.example.kingdee.exception.ProcedureException;
import com.example.kingdee.sqlTool.SqlTool;
import com.example.kingdee.sqlTool.sqltoolEnum.FormIdEnum;
import com.jushuitan.api.ApiClient;
import com.jushuitan.api.ApiRequest;
import com.jushuitan.api.ApiResponse;
import com.jushuitan.api.DefaultApiClient;
import com.jushuitan.api.exception.ApiException;
import com.jushuitan.api.util.ApiUtil;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.*;
import java.util.zip.GZIPInputStream;

/**
 * @description: 聚水潭工具类
 * @author: WangYX
 * @create: 2021-12-30 17:11
 * @Version: 1.0.0
 **/
@Component
public class JuShuiTanUtil {

    private static Logger logger = Logger.getLogger(JuShuiTanUtil.class);
    public static final String SIGN_METHOD_MD5 = "md5";
    public static final String CHARSET_UTF8 = "utf-8";
    public static final String CONTENT_ENCODING_GZIP = "gzip";
    private static final int connectTimeout = 2000;
    private static final int readTimeout = 40000;

    /**
     * 请求地址
     */
    public static String serverUrl;

    /**
     * 开放平台地址
     */
    public static String openWebUrl;

    /**
     * 开放平台地址
     */
    public static String openApiUrl;

    /**
     * 应用的appKey
     */
    public static String appKey;

    /**
     * 应用的appSecret
     */
    public static String appSecret;

    @Autowired
    private RuoYiConfig ruoYiConfig;

    @PostConstruct
    public void init() {
        serverUrl = ruoYiConfig.getServerUrl();
        openWebUrl = ruoYiConfig.getOpenWebUrl();
        openApiUrl = ruoYiConfig.getOpenApiUrl();
        appKey = ruoYiConfig.getAppKey();
        appSecret = ruoYiConfig.getAppSecret();
    }

    //发送查询的请求
    public static String sendQueryHttp(String url, HashMap<String, Object> params, String accessToken){
        String response = sendOpenApiHttp(url, params, accessToken);
        JSONObject responseObj = JSONObject.parseObject(response);
        JSONObject shopObjData = JSONObject.parseObject(responseObj.get("data").toString());
        if (shopObjData.get("datas") != null){
            return shopObjData.get("datas").toString();
        }else {
            return null;
        }
    }

    public static String sendSalQueryHttp(String url, HashMap<String, Object> params, String accessToken){
        String response = sendOpenApiHttp(url, params, accessToken);
        JSONObject responseObj = JSONObject.parseObject(response);
        JSONObject shopObjData = JSONObject.parseObject(responseObj.get("data").toString());
        if (shopObjData.get("orders") != null){
            return shopObjData.get("orders").toString();
        }else {
            return null;
        }
    }

    //查询仓库
    public static String getStockInfo(String url, HashMap<String, Object> params, String accessToken){
        String response = sendOpenApiHttp(url, params, accessToken);
        JSONObject responseObj = JSONObject.parseObject(response);
        JSONObject shopObjData = JSONObject.parseObject(responseObj.get("data").toString());
        if (shopObjData.get("datas") != null){
            return shopObjData.get("datas").toString();
        }else {
            return null;
        }
    }

    //发送查询的请求
    public static JSONObject sendQueryHttpBody(String url, HashMap<String, Object> params, String accessToken){
        String response = sendOpenApiHttp(url, params, accessToken);
        return JSONObject.parseObject(response);
    }

    //发送聚水潭api请求请求
    public static String sendOpenApiHttp(String url, HashMap<String, Object> params, String accessToken){
        // 实例化client
        ApiClient client = new DefaultApiClient();
        // 设置超时时间
        ((DefaultApiClient)client).setReadTimeout(3000);
        ((DefaultApiClient)client).setConnectTimeout(2000);
        // 构建请求对象
        ApiRequest request = new ApiRequest.Builder(url, appKey, appSecret).biz(JSON.toJSONString(params)).build();

        // 执行接口调用
        try {
            ApiResponse response = client.execute(request, accessToken);
            boolean success = response.isSuccess();
            if (success){
                JSONObject jsonObject = JSONObject.parseObject(response.getBody());
                if (!jsonObject.get("code").toString().equals("0")){
                    logger.error(SqlTool.modifyItemSyncLogInfo(new ArrayList<>(), YiTiConstant.JST_API_ERROR, "失败", jsonObject.get("msg").toString()));
                    throw new ProcedureException("调用聚水潭接口失败:" +  jsonObject.get("msg").toString());
                }
                return response.getBody();
            }else {
                logger.error(SqlTool.modifyItemSyncLogInfo(new ArrayList<>(), YiTiConstant.JST_API_ERROR, "失败", response.getMsg()));
                throw new ProcedureException(response.getMsg());
//                return response.getBody();
            }

        } catch (ApiException e) {
            switch (e.getCodeEnum()) {
                case SYS_ERROR:
                    // TODO retry
                    break;
                case PARAM_ERROR:
                    // TODO check param
                    break;
                case SIGN_ERROR:
                    // TODO @see com.jushuitan.api.util.ApiUtil
                    break;
                case CONNECT_ERROR:
                    // TODO check network
                    break;
                case TIME_OUT_ERROR:
                    // TODO check logic or reset readTimeout
                    break;
                case EXECUTE_ERROR:
                    // TODO @see com.jushuitan.api.DefaultApiClient#executeRemote
                    break;
                default:
                    // log
                    break;
            }
            throw new ProcedureException("调用聚水潭接口失败");
        } catch (Exception e) {
            e.printStackTrace();
            throw new ProcedureException("调用聚水潭接口失败");
        }
    }

    //发送聚水潭api请求请求
    public static String sendOpenApiHttp(String url, List<HashMap<String, Object>> params, String accessToken){
        // 实例化client
        ApiClient client = new DefaultApiClient();
        // 设置超时时间
        ((DefaultApiClient)client).setReadTimeout(3000);
        ((DefaultApiClient)client).setConnectTimeout(2000);
        // 构建请求对象
        ApiRequest request = new ApiRequest.Builder(url, appKey, appSecret).biz(JSON.toJSONString(params)).build();

        // 执行接口调用
        try {
            ApiResponse response = client.execute(request, accessToken);
            boolean success = response.isSuccess();
            if (success){
                JSONObject jsonObject = JSONObject.parseObject(response.getBody());
                if (!jsonObject.get("code").toString().equals("0")){
                    logger.error(SqlTool.modifyItemSyncLogInfo(new ArrayList<>(), YiTiConstant.JST_API_ERROR, "失败", jsonObject.get("msg").toString()));
                    throw new ProcedureException("调用聚水潭接口失败:" +  jsonObject.get("msg").toString());
                }
                return response.getBody();
            }else {
                logger.error(SqlTool.modifyItemSyncLogInfo(new ArrayList<>(), YiTiConstant.JST_API_ERROR, "失败", response.getMsg()));
                throw new ProcedureException(response.getMsg());
//                return response.getBody();
            }

        } catch (ApiException e) {
            switch (e.getCodeEnum()) {
                case SYS_ERROR:
                    // TODO retry
                    break;
                case PARAM_ERROR:
                    // TODO check param
                    break;
                case SIGN_ERROR:
                    // TODO @see com.jushuitan.api.util.ApiUtil
                    break;
                case CONNECT_ERROR:
                    // TODO check network
                    break;
                case TIME_OUT_ERROR:
                    // TODO check logic or reset readTimeout
                    break;
                case EXECUTE_ERROR:
                    // TODO @see com.jushuitan.api.DefaultApiClient#executeRemote
                    break;
                default:
                    // log
                    break;
            }
            throw new ProcedureException("调用聚水潭接口失败");
        } catch (Exception e) {
            e.printStackTrace();
            throw new ProcedureException("调用聚水潭接口失败");
        }
    }

    /**
     * 构建表单请求的body
     *
     * @param params
     * @param charset
     * @return
     * @throws IOException
     */
    public static String buildQuery(Map<String, String> params, String charset) throws IOException {
        if (params == null || params.isEmpty()) {
            return null;
        }

        StringBuilder query = new StringBuilder();
        Set<Map.Entry<String, String>> entries = params.entrySet();
        boolean hasParam = false;

        for (Map.Entry<String, String> entry : entries) {
            String name = entry.getKey();
            String value = entry.getValue().toString();
            // 忽略参数名或参数值为空的参数
            if (isNotEmpty(name) && isNotEmpty(value)) {
                if (hasParam) {
                    query.append("&");
                } else {
                    hasParam = true;
                }
                query.append(name).append("=").append(URLEncoder.encode(value, charset));
            }
        }

        return query.toString();
    }

    /**
     * 获取accessToken和refreshToken
     */
    public static String getAccessToken(String code){
        String url="https://openapi.jushuitan.com/openWeb/auth/getInitToken";
        // 设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        RestTemplate restTemplate=new RestTemplate();

        String timestamp = String.valueOf(System.currentTimeMillis() / 1000L);
        HashMap<String, String> params = new LinkedHashMap<>();
        params.put("app_key", appKey);
        params.put("timestamp", timestamp);
        params.put("grant_type", "authorization_code");
        params.put("charset", "utf-8");
        params.put("code", code);

        MultiValueMap<String, String> forms= new LinkedMultiValueMap<String, String>();
        forms.put("app_key", Collections.singletonList(appKey));
        forms.put("timestamp", Collections.singletonList(timestamp));
        forms.put("grant_type", Collections.singletonList("authorization_code"));
        forms.put("charset", Collections.singletonList("utf-8"));
        forms.put("code", Collections.singletonList(code));
        String sign = ApiUtil.getSign(appSecret, params);
        forms.put("sign", Collections.singletonList(sign));

        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<MultiValueMap<String, String>>(forms, headers);
        //获取返回数据
        return restTemplate.postForObject(url, httpEntity, String.class);
    }

    /**
     * Function：刷新AccessToken
     *
     * @author Wangyx
     * @date 2022/01/04 9:18
     * @version 1.0.0
     */
    public static String refreshToken(String refreshToken) {
        String url="https://openapi.jushuitan.com/openWeb/auth/refreshToken";
        // 设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        RestTemplate restTemplate = new RestTemplate();

        String timestamp = String.valueOf(System.currentTimeMillis() / 1000L);
        HashMap<String, String> params = new LinkedHashMap<>();
        params.put("app_key", appKey);
        params.put("timestamp", timestamp);
        params.put("grant_type", "refresh_token");
        params.put("charset", "utf-8");
        params.put("refresh_token", refreshToken);
        params.put("scope", "all");

        MultiValueMap<String, String> forms= new LinkedMultiValueMap<String, String>();
        forms.put("app_key", Collections.singletonList(appKey));
        forms.put("timestamp", Collections.singletonList(timestamp));
        forms.put("grant_type", Collections.singletonList("refresh_token"));
        forms.put("charset", Collections.singletonList("utf-8"));
        forms.put("refresh_token", Collections.singletonList(refreshToken));
        forms.put("scope", Collections.singletonList("all"));
        String sign = ApiUtil.getSign(appSecret, params);
        forms.put("sign", Collections.singletonList(sign));

        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<MultiValueMap<String, String>>(forms, headers);
        //获取返回数据
        return restTemplate.postForObject(url, httpEntity, String.class);
    }

    public static ApiResponse executeRemote(String urlParam, Map<String, String> params) throws IOException, ApiException {
        String query = buildQuery(params, "utf-8");
        byte[] content = new byte[0];
        if (query != null) {
            content = query.getBytes("utf-8");
        }

        URL url = new URL(urlParam);
        HttpURLConnection conn = null;
        OutputStream out = null;

        ApiResponse var8;
        try {
            conn = (HttpURLConnection)url.openConnection();
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setConnectTimeout(connectTimeout);
            conn.setReadTimeout(readTimeout);
            conn.setRequestProperty("open-source", "sdk");
            conn.setRequestProperty("Host", url.getHost());
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
            out = conn.getOutputStream();
            out.write(content);
            var8 = getResponseAsString(conn);
        } finally {
            if (out != null) {
                out.close();
            }

            if (conn != null) {
                conn.disconnect();
            }

        }

        return var8;
    }

    private static ApiResponse getResponseAsString(HttpURLConnection conn) throws IOException {
        ApiResponse response = new ApiResponse();
        String charset = "utf-8";
        if (conn.getResponseCode() < 400) {
            String contentEncoding = conn.getContentEncoding();
            if ("gzip".equalsIgnoreCase(contentEncoding)) {
                response.setBody(getStreamAsString(new GZIPInputStream(conn.getInputStream()), charset));
            } else {
                response.setBody(getStreamAsString(conn.getInputStream(), charset));
            }
        } else {
            response.setCode(conn.getResponseCode());
            response.setMsg(conn.getResponseMessage());
        }

        return response;
    }

    private static String getStreamAsString(InputStream stream, String charset) throws IOException {
        try {
            Reader reader = new InputStreamReader(stream, charset);
            StringBuilder response = new StringBuilder();
            char[] buff = new char[1024];
//            boolean read = false;

            int read;
            while((read = reader.read(buff)) > 0) {
                response.append(buff, 0, read);
            }

            String var7 = response.toString();
            return var7;
        } finally {
            if (stream != null) {
                stream.close();
            }

        }
    }


    public static String signTopRequest(Map<String, String> params, String app_secret) {
        try {
            String sortedStr = getSortedParamStr(params);
            String paraStr = app_secret + sortedStr;

            return createSign(paraStr);
        } catch (UnsupportedEncodingException e) {
            throw new ProcedureException("更新失败");
        }

    }

    /**
     * 构造自然排序请求参数
     *
     * @param params 请求
     * @return 字符串
     */
    private static String getSortedParamStr(Map<String, String> params) throws UnsupportedEncodingException {
        // 第一步：检查参数是否已经排序
        String[] keys = params.keySet().toArray(new String[0]);
        Arrays.sort(keys);

//        Set<String> sortedParams = new TreeSet<>(params.keySet());

        StringBuilder strB = new StringBuilder();
        // 排除sign和空值参数
        for (String key : keys) {
            if ("sign".equalsIgnoreCase(key)) {
                continue;
            }

            String value = params.get(key).toString();

            if (StringUtils.isNotEmpty(value)) {
                strB.append(key).append(value);
            }
        }
        return strB.toString();
    }

    /**
     * 生成新sign
     *
     * @param str 字符串
     * @return String
     */
    private static String createSign(String str) {
        if (str == null || str.length() == 0) {
            return null;
        }
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        try {
            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(str.getBytes("UTF-8"));

            byte[] md = mdTemp.digest();
            int j = md.length;
            char[] buf = new char[j * 2];
            int k = 0;
            int i = 0;
            while (i < j) {
                byte byte0 = md[i];
                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
                buf[k++] = hexDigits[byte0 & 0xf];
                i++;
            }
            return new String(buf);
        } catch (Exception e) {
            throw new ProcedureException("失败");
        }
    }

    /**
     * 创建店铺授权聚水潭的URL
     *
     * @return
     */
    public static String spliceAuthorizationUrl(String state) throws IOException {
        Map<String, String> params = new HashMap<String, String>();
        // 公共参数
        params.put("app_key", appKey);
        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        params.put("state", state);
        params.put("charset", "utf-8");
        // 签名参数
        params.put("sign", signTopRequest(params, appSecret));
        String url = buildQuery(params, "utf-8");
        url = openWebUrl + "/auth?" + url;
        return url;
    }

    public static boolean isNotEmpty(String value) {
        int strLen;
        if (value == null || (strLen = value.length()) == 0) {
            return false;
        }
        for (int i = 0; i < strLen; i++) {
            if ((Character.isWhitespace(value.charAt(i)) == false)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 发送请求
     *
     * @param accessToken
     * @param biz
     * @param requestUrl
     * @return
     * @throws IOException
     */
    public static String sendRequest(String accessToken, String biz, String requestUrl) throws IOException {
        // 公共请求参数
        HashMap<String, String> params = new HashMap<String, String>();
        params.put("app_key", appKey);
        params.put("access_token", accessToken);
        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        params.put("version", "2");
        params.put("charset", "utf-8");
        // 业务参数
        params.put("biz", biz);
        // 签名参数
        params.put("sign", signTopRequest(params, appSecret));
        String url = serverUrl + requestUrl;
        return SqlTool.sendHttpPost(url, params);
    }

    /**
     * 获取分页数
     *
     * @return
     * @throws IOException
     */
    public static Integer getOrderInfoPage(PurchaseSyncDto purchaseSyncDto, FormIdEnum httpUrl, String accessToken) {
        HashMap<String, Object> params = new HashMap<>();
        //指定查询起始页
        params.put("page_index", purchaseSyncDto.getIndex() == null ? 1 : purchaseSyncDto.getIndex());
        //指定每页查询的数据量
        params.put("page_size", purchaseSyncDto.getPageSize() == null ? 50 : purchaseSyncDto.getPageSize());
        //指定查询开始结束时间
        params.put("modified_begin", purchaseSyncDto.getStarTimeStr());
        params.put("modified_end", purchaseSyncDto.getEndTimeStr());
        String url = openApiUrl + FormIdEnum.getDBTableName(httpUrl.name());
        JSONObject jsonObject = JuShuiTanUtil.sendQueryHttpBody(url, params, accessToken);
        JSONObject data = JSONObject.parseObject(jsonObject.get("data").toString());
        Object pageCount = data.get("page_count");
        return Integer.parseInt(pageCount.toString());
    }

    /**
     * 获取分页数
     *
     * @return
     * @throws IOException
     */
    public static Integer getSalOrderInfoPage(PurchaseSyncDto purchaseSyncDto, FormIdEnum httpUrl, String accessToken) {
        HashMap<String, Object> params = new HashMap<>();
        //指定单据状态
        params.put("status", purchaseSyncDto.getStatusList().get(0));
        //制定查询采购单号
        if (purchaseSyncDto.getPoIds() != null && purchaseSyncDto.getPoIds().size() != 0){
            params.put("o_ids", purchaseSyncDto.getPoIds());
        }
        //指定查询起始页
        params.put("page_index", purchaseSyncDto.getIndex() == null ? 1 : purchaseSyncDto.getIndex());
        //指定每页查询的数据量
        params.put("page_size", purchaseSyncDto.getPageSize() == null ? 50 : purchaseSyncDto.getPageSize());
        //指定查询开始结束时间
        params.put("modified_begin", purchaseSyncDto.getStarTimeStr());
        params.put("modified_end", purchaseSyncDto.getEndTimeStr());
        String url = openApiUrl + FormIdEnum.getDBTableName(httpUrl.name());
        JSONObject jsonObject = JuShuiTanUtil.sendQueryHttpBody(url, params, accessToken);
        JSONObject data = JSONObject.parseObject(jsonObject.get("data").toString());
        Object pageCount = data.get("page_count");
        return Integer.parseInt(pageCount.toString());
    }

    /**
     * 获取分页数
     *
     * @return
     * @throws IOException
     */
    public static Integer getRefundInfoPage(PurchaseSyncDto purchaseSyncDto, FormIdEnum httpUrl, String accessToken) {
        HashMap<String, Object> params = new HashMap<>();
        //指定单据状态
        params.put("status", purchaseSyncDto.getStatusList().get(0));
        //制定查询采购单号
        if (purchaseSyncDto.getPoIds() != null && purchaseSyncDto.getPoIds().size() != 0){
            params.put("as_ids", purchaseSyncDto.getPoIds());
        }
        //指定查询起始页
        params.put("page_index", purchaseSyncDto.getIndex() == null ? 1 : purchaseSyncDto.getIndex());
        //指定每页查询的数据量
        params.put("page_size", purchaseSyncDto.getPageSize() == null ? 50 : purchaseSyncDto.getPageSize());
        //指定查询开始结束时间
        params.put("modified_begin", purchaseSyncDto.getStarTimeStr());
        params.put("modified_end", purchaseSyncDto.getEndTimeStr());
        String url = openApiUrl + FormIdEnum.getDBTableName(httpUrl.name());
        JSONObject jsonObject = JuShuiTanUtil.sendQueryHttpBody(url, params, accessToken);
        JSONObject data = JSONObject.parseObject(jsonObject.get("data").toString());
        Object pageCount = data.get("page_count");
        return Integer.parseInt(pageCount.toString());
    }
}
